//! Universal Serial Bus (USB)

#[cfg_attr(usb, path = "usb.rs")]
#[cfg_attr(otg, path = "otg.rs")]
mod _version;
pub use _version::*;

use crate::interrupt::typelevel::Interrupt;
use crate::rcc;

/// clock, power initialization stuff that's common for USB and OTG.
fn common_init<T: Instance>() {
    // Check the USB clock is enabled and running at exactly 48 MHz.
    // frequency() will panic if not enabled
    let freq = T::frequency();
    // Check frequency is within the 0.25% tolerance allowed by the spec.
    // Clock might not be exact 48Mhz due to rounding errors in PLL calculation, or if the user
    // has tight clock restrictions due to something else (like audio).
    if freq.0.abs_diff(48_000_000) > 120_000 {
        panic!(
            "USB clock should be 48Mhz but is {} Hz. Please double-check your RCC settings.",
            freq.0
        )
    }

    #[cfg(any(stm32l4, stm32l5, stm32wb, stm32u0))]
    critical_section::with(|_| crate::pac::PWR.cr2().modify(|w| w.set_usv(true)));

    #[cfg(pwr_h5)]
    critical_section::with(|_| crate::pac::PWR.usbscr().modify(|w| w.set_usb33sv(true)));

    #[cfg(stm32h7)]
    {
        // If true, VDD33USB is generated by internal regulator from VDD50USB
        // If false, VDD33USB and VDD50USB must be suplied directly with 3.3V (default on nucleo)
        // TODO: unhardcode
        let internal_regulator = false;

        // Enable USB power
        critical_section::with(|_| {
            crate::pac::PWR.cr3().modify(|w| {
                w.set_usb33den(true);
                w.set_usbregen(internal_regulator);
            })
        });

        // Wait for USB power to stabilize
        while !crate::pac::PWR.cr3().read().usb33rdy() {}
    }

    #[cfg(stm32u5)]
    {
        // Enable USB power
        critical_section::with(|_| {
            crate::pac::PWR.svmcr().modify(|w| {
                w.set_usv(true);
                w.set_uvmen(true);
            })
        });

        // Wait for USB power to stabilize
        while !crate::pac::PWR.svmsr().read().vddusbrdy() {}
    }

    T::Interrupt::unpend();
    unsafe { T::Interrupt::enable() };

    rcc::enable_and_reset::<T>();
}
